/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2009 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "commonheaders.h"
#include "findadd.h"

enum {
	COLUMNID_PROTO,
	COLUMNID_HANDLE,
	COLUMNID_NICK,
	COLUMNID_FIRST,
	COLUMNID_LAST,
	COLUMNID_EMAIL,
	NUM_COLUMNID
};

void SaveColumnSizes(HWND hwndResults)
{
	int columnOrder[NUM_COLUMNID];
	int columnCount;
	char szSetting[32];
	int i;
	struct FindAddDlgData *dat;

	dat=(struct FindAddDlgData*)GetWindowLongPtr(GetParent(hwndResults),GWLP_USERDATA);
	columnCount=Header_GetItemCount(ListView_GetHeader(hwndResults));
	if (columnCount != NUM_COLUMNID) return;
	ListView_GetColumnOrderArray(hwndResults,columnCount,columnOrder);
	for(i=0; i < NUM_COLUMNID; i++) {
		mir_snprintf(szSetting, SIZEOF(szSetting), "ColOrder%d", i);
		DBWriteContactSettingByte(NULL,"FindAdd",szSetting,(BYTE)columnOrder[i]);
		if(i>=columnCount) continue;
		mir_snprintf(szSetting, SIZEOF(szSetting), "ColWidth%d", i);
		DBWriteContactSettingWord(NULL,"FindAdd",szSetting,(WORD)ListView_GetColumnWidth(hwndResults,i));
	}
	DBWriteContactSettingByte(NULL,"FindAdd","SortColumn",(BYTE)dat->iLastColumnSortIndex);
	DBWriteContactSettingByte(NULL,"FindAdd","SortAscending",(BYTE)dat->bSortAscending);
}

static const TCHAR *szColumnNames[] = { NULL, NULL, _T("Nick"), _T("First Name"), _T("Last Name"), _T("E-mail") };
static int defaultColumnSizes[]={0,90,100,100,100,2000};
void LoadColumnSizes(HWND hwndResults,const char *szProto)
{
	HDITEM hdi;
	int columnOrder[NUM_COLUMNID];
	int columnCount;
	char szSetting[32];
	int i;
	FindAddDlgData *dat;
	bool colOrdersValid;

	defaultColumnSizes[COLUMNID_PROTO] = GetSystemMetrics(SM_CXSMICON) + 4;
	dat = (FindAddDlgData*)GetWindowLongPtr(GetParent(hwndResults), GWLP_USERDATA);

	columnCount = NUM_COLUMNID;
	colOrdersValid = true;
	for(i=0; i < NUM_COLUMNID; i++) 
	{
		LVCOLUMN lvc;
		if( i < columnCount ) 
		{
			int bNeedsFree = FALSE;
			lvc.mask = LVCF_TEXT | LVCF_WIDTH;
			if( szColumnNames[i] != NULL )
				lvc.pszText = TranslateTS( szColumnNames[i] );
			else if( i == COLUMNID_HANDLE ) 
			{
				if (szProto)
				{
					#if defined( _UNICODE )
						bNeedsFree = TRUE;
						lvc.pszText = mir_a2t((char*)CallProtoService(szProto,PS_GETCAPS,PFLAG_UNIQUEIDTEXT,0));
					#else
						lvc.pszText = (char*)CallProtoService(szProto,PS_GETCAPS,PFLAG_UNIQUEIDTEXT,0);
					#endif
				}
				else
					lvc.pszText = _T("ID");
			}
			else lvc.mask &= ~LVCF_TEXT;
			mir_snprintf(szSetting, SIZEOF(szSetting), "ColWidth%d", i);
			lvc.cx = DBGetContactSettingWord(NULL, "FindAdd", szSetting, defaultColumnSizes[i]);
			ListView_InsertColumn(hwndResults, i, (LPARAM)&lvc);
			#if defined( _UNICODE )
				if (bNeedsFree)
					mir_free(lvc.pszText);
			#endif
		}
		mir_snprintf(szSetting, SIZEOF(szSetting), "ColOrder%d", i);
		columnOrder[i] = DBGetContactSettingByte(NULL, "FindAdd", szSetting, -1);
		if (columnOrder[i] == -1 || columnOrder[i] >= NUM_COLUMNID) colOrdersValid = false;
	}

	if (colOrdersValid) 
		ListView_SetColumnOrderArray(hwndResults, columnCount, columnOrder);

	dat->iLastColumnSortIndex = DBGetContactSettingByte(NULL, "FindAdd", "SortColumn", COLUMNID_NICK);
	if (dat->iLastColumnSortIndex >= columnCount) dat->iLastColumnSortIndex = COLUMNID_NICK;
	dat->bSortAscending = DBGetContactSettingByte(NULL, "FindAdd", "SortAscending", TRUE);

	hdi.mask = HDI_BITMAP | HDI_FORMAT;
	hdi.fmt = HDF_LEFT | HDF_BITMAP | HDF_STRING | HDF_BITMAP_ON_RIGHT;
	hdi.hbm = dat->bSortAscending ? dat->hBmpSortDown : dat->hBmpSortUp;
	Header_SetItem(ListView_GetHeader(hwndResults), dat->iLastColumnSortIndex, &hdi);
}

static LPARAM ListView_GetItemLParam(HWND hwndList, int idx)
{
	LVITEM lv;
	lv.iItem = idx;
	lv.mask = LVIF_PARAM;
	ListView_GetItem(hwndList,&lv);
	return lv.lParam;
}

int CALLBACK SearchResultsCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	struct FindAddDlgData *dat=(struct FindAddDlgData*)GetWindowLongPtr((HWND) lParamSort, GWLP_USERDATA);
	int sortMultiplier;
	int sortCol;
	struct ListSearchResult *lsr1, *lsr2;
	HWND hList=GetDlgItem((HWND) lParamSort, IDC_RESULTS);

	sortMultiplier=dat->bSortAscending?1:-1;
	sortCol=dat->iLastColumnSortIndex;
	if (!dat->bFlexSearchResult)
	{
		lsr1=(struct ListSearchResult*)ListView_GetItemLParam(hList, (int)lParam1);
		lsr2=(struct ListSearchResult*)ListView_GetItemLParam(hList, (int)lParam2);

		if ( lsr1 == NULL || lsr2 == NULL ) return 0;
		switch(sortCol)
		{
		case COLUMNID_PROTO:
			return lstrcmpA(lsr1->szProto, lsr2->szProto)*sortMultiplier;
		case COLUMNID_HANDLE:
			return lstrcmpi(lsr1->psr.id, lsr2->psr.id)*sortMultiplier;
		case COLUMNID_NICK:
			return lstrcmpi(lsr1->psr.nick, lsr2->psr.nick)*sortMultiplier;
		case COLUMNID_FIRST:
			return lstrcmpi(lsr1->psr.firstName, lsr2->psr.firstName)*sortMultiplier;
		case COLUMNID_LAST:
			return lstrcmpi(lsr1->psr.lastName, lsr2->psr.lastName)*sortMultiplier;
		case COLUMNID_EMAIL:
			return lstrcmpi(lsr1->psr.email, lsr2->psr.email)*sortMultiplier;
		}
	}
	else 
	{
		TCHAR szText1[100];
		TCHAR szText2[100];
		ListView_GetItemText(hList,(int)lParam1,sortCol,szText1,SIZEOF(szText1));
		ListView_GetItemText(hList,(int)lParam2,sortCol,szText2,SIZEOF(szText2));
		return _tcsicmp(szText1, szText2)*sortMultiplier;
	}
	return 0;
}

void FreeSearchResults(HWND hwndResults)
{
	LV_ITEM lvi;
	struct ListSearchResult *lsr;
	for(lvi.iItem=ListView_GetItemCount(hwndResults)-1;lvi.iItem>=0;lvi.iItem--) {
		lvi.mask=LVIF_PARAM;
		ListView_GetItem(hwndResults,&lvi);
		lsr=(struct ListSearchResult*)lvi.lParam;
		if(lsr==NULL) continue;
		mir_free(lsr->psr.id);
		mir_free(lsr->psr.email);
		mir_free(lsr->psr.nick);
		mir_free(lsr->psr.firstName);
		mir_free(lsr->psr.lastName);
		mir_free(lsr);
	}
	ListView_DeleteAllItems(hwndResults);
	EnableResultButtons(GetParent(hwndResults),0);
}

// on its own thread
static void BeginSearchFailed(void * arg)
{
	TCHAR buf[128];
	if ( arg != NULL ) {
		const TCHAR* protoName = (TCHAR*)arg;
		mir_sntprintf(buf,SIZEOF(buf),
            TranslateT("Could not start a search on '%s', there was a problem - is %s connected?"),
            protoName,protoName);
		mir_free((char*)arg);
	}
	else lstrcpyn(buf,TranslateT("Could not search on any of the protocols, are you online?"),SIZEOF(buf));
	MessageBox(0,buf,TranslateT("Problem with search"),MB_OK | MB_ICONERROR);
}

int BeginSearch(HWND,struct FindAddDlgData *dat,const char *szProto,const char *szSearchService,DWORD requiredCapability,void *pvSearchParams)
{
	int i;
	if ( szProto == NULL ) {
		int failures = 0;
		dat->searchCount = 0;
		dat->search = (struct ProtoSearchInfo*)mir_calloc(sizeof(struct ProtoSearchInfo) * accounts.getCount());
		for( i=0; i < accounts.getCount();i++) {
			PROTOACCOUNT* pa = accounts[i];
			if (!Proto_IsAccountEnabled(pa)) continue;
			DWORD caps=(DWORD)CallProtoService(pa->szModuleName,PS_GETCAPS,PFLAGNUM_1,0);
			if(!(caps&requiredCapability)) continue;
			dat->search[dat->searchCount].hProcess = (HANDLE)CallProtoService(pa->szModuleName,szSearchService,0,(LPARAM)pvSearchParams);
			dat->search[dat->searchCount].szProto = pa->szModuleName;
			if ( dat->search[dat->searchCount].hProcess == NULL ) failures++;
			else dat->searchCount++;
		}
		if(failures) {
			//infuriatingly vague error message. fixme.
			if(dat->searchCount==0) {
				forkthread(BeginSearchFailed,0,NULL);
				mir_free(dat->search);
				dat->search=NULL;
				return 1;
		}	}
	}
	else {
		dat->search=(struct ProtoSearchInfo*)mir_alloc(sizeof(struct ProtoSearchInfo));
		dat->searchCount=1;
		dat->search[0].hProcess=(HANDLE)CallProtoService(szProto,szSearchService,0,(LPARAM)pvSearchParams);
		dat->search[0].szProto=szProto;
		if(dat->search[0].hProcess==NULL) {
			//infuriatingly vague error message. fixme.
            PROTOACCOUNT* pa = Proto_GetAccount(szProto);
			forkthread(BeginSearchFailed, 0, mir_tstrdup(pa->tszAccountName));
			mir_free(dat->search);
			dat->search=NULL;
			dat->searchCount=0;
			return 1;
		}
	}
	return 0;
}

// !!!!!!!! this code is dangerous like a hell
void SetStatusBarSearchInfo(HWND hwndStatus,struct FindAddDlgData *dat)
{
	TCHAR str[256];

	if (dat->searchCount != 0 ) {
		int i;

		lstrcpy( str, TranslateT("Searching"));
		for( i=0; i < dat->searchCount; i++ ) {
			PROTOACCOUNT* pa = Proto_GetAccount( dat->search[i].szProto );
			if ( !pa )
				continue;

			lstrcat(str, i ? _T(",") : _T( " " ));
			lstrcat(str, pa->tszAccountName );
	}	}
	else lstrcpy(str, TranslateT("Idle"));
		
	SendMessage( hwndStatus, SB_SETTEXT, 0, (LPARAM)str );
}	

struct ProtoResultsSummary {
	const char *szProto;
	int count;
};
void SetStatusBarResultInfo(HWND hwndDlg)
{
	HWND hwndStatus=GetDlgItem(hwndDlg,IDC_STATUSBAR);
	HWND hwndResults=GetDlgItem(hwndDlg,IDC_RESULTS);
	LV_ITEM lvi;
	struct ListSearchResult *lsr;
	struct ProtoResultsSummary *subtotal=NULL;
	int subtotalCount=0;
	int i,total;
	TCHAR str[256];

	total=ListView_GetItemCount(hwndResults);
	for(lvi.iItem=total-1;lvi.iItem>=0;lvi.iItem--) {
		lvi.mask=LVIF_PARAM;
		ListView_GetItem(hwndResults,&lvi);
		lsr=(struct ListSearchResult*)lvi.lParam;
		if(lsr==NULL) continue;
		for(i=0;i<subtotalCount;i++) {
			if(subtotal[i].szProto==lsr->szProto) {
				subtotal[i].count++;
				break;
			}
		}
		if(i==subtotalCount) {
			subtotal=(struct ProtoResultsSummary*)mir_realloc(subtotal,sizeof(struct ProtoResultsSummary)*(subtotalCount+1));
			subtotal[subtotalCount].szProto=lsr->szProto;
			subtotal[subtotalCount++].count=1;
		}
	}
	if ( total != 0 ) {
		TCHAR substr[64];
		PROTOACCOUNT* pa = Proto_GetAccount( subtotal[0].szProto );
		if ( pa == NULL )
			return;

		if ( subtotalCount == 1 ) {
			if(total==1) mir_sntprintf( str, SIZEOF(str), TranslateT("1 %s user found"), pa->tszAccountName );
			else         mir_sntprintf( str, SIZEOF(str), TranslateT("%d %s users found"), total, pa->tszAccountName );
		}
		else {
			mir_sntprintf( str, SIZEOF(str), TranslateT("%d users found ("),total);
			for( i=0; i < subtotalCount; i++ ) {
				if ( i ) {
					if (( pa = Proto_GetAccount( subtotal[i].szProto )) == NULL )
						return;
					lstrcat( str, _T(", "));
				}
				mir_sntprintf( substr, SIZEOF(substr), _T("%d %s"), subtotal[i].count, pa->tszAccountName );
				lstrcat( str, substr );
			}
			lstrcat( str, _T(")"));
		}
		mir_free(subtotal);
	}
	else lstrcpy(str, TranslateT("No users found"));
	SendMessage(hwndStatus, SB_SETTEXT, 2, (LPARAM)str );
}

void CreateResultsColumns(HWND hwndResults,struct FindAddDlgData *dat,char *szProto)
{
	SaveColumnSizes(hwndResults);
	while(ListView_DeleteColumn(hwndResults,0));
	ListView_SetImageList(hwndResults,dat->himlComboIcons,LVSIL_SMALL);
	LoadColumnSizes(hwndResults,szProto);
}

void ShowMoreOptionsMenu(HWND hwndDlg,int x,int y)
{
	struct FindAddDlgData *dat;
	HMENU hPopupMenu,hMenu;
	int commandId;
	struct ListSearchResult *lsr;

	dat=(struct FindAddDlgData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);

	{	LVITEM lvi;
		if(ListView_GetSelectedCount(GetDlgItem(hwndDlg,IDC_RESULTS))!=1) return;
		lvi.mask=LVIF_PARAM;
		lvi.iItem=ListView_GetNextItem(GetDlgItem(hwndDlg,IDC_RESULTS),-1,LVNI_ALL|LVNI_SELECTED);
		ListView_GetItem(GetDlgItem(hwndDlg,IDC_RESULTS),&lvi);
		lsr=(struct ListSearchResult*)lvi.lParam;
	}

	hMenu=LoadMenu(hMirandaInst,MAKEINTRESOURCE(IDR_CONTEXT));
	hPopupMenu=GetSubMenu(hMenu,4);
	CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)hPopupMenu,0);
	commandId=TrackPopupMenu(hPopupMenu,TPM_RIGHTBUTTON|TPM_RETURNCMD,x,y,0,hwndDlg,NULL);
	switch(commandId) {
		case IDC_ADD:
		{	ADDCONTACTSTRUCT acs;

			acs.handle=NULL;
			acs.handleType=HANDLE_SEARCHRESULT;
			acs.szProto=lsr->szProto;
			acs.psr=&lsr->psr;
			CallService(MS_ADDCONTACT_SHOW,(WPARAM)hwndDlg,(LPARAM)&acs);
			break;
		}
		case IDC_DETAILS:
		{	HANDLE hContact;
			hContact=(HANDLE)CallProtoService(lsr->szProto,PS_ADDTOLIST,PALF_TEMPORARY,(LPARAM)&lsr->psr);
			CallService(MS_USERINFO_SHOWDIALOG,(WPARAM)hContact,0);
			break;
		}
		case IDC_SENDMESSAGE:
		{	HANDLE hContact;
			hContact=(HANDLE)CallProtoService(lsr->szProto,PS_ADDTOLIST,PALF_TEMPORARY,(LPARAM)&lsr->psr);
			CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)(const char*)NULL);
			break;
		}
	}
	DestroyMenu(hPopupMenu);
	DestroyMenu(hMenu);
}


